
package com.jf.cloud.search.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.order.constant.OrderStatus;
import com.jf.cloud.api.vo.EsPageVO;
import com.jf.cloud.common.constant.Constant;
import com.jf.cloud.common.constant.PayType;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.i18n.I18nMessage;
import com.jf.cloud.common.i18n.LanguageEnum;
import com.jf.cloud.common.order.bo.EsOrderBO;
import com.jf.cloud.common.order.bo.EsOrderItemBO;
import com.jf.cloud.common.order.constant.OrderType;
import com.jf.cloud.common.order.dto.OrderSearchDTO;
import com.jf.cloud.common.order.vo.EsOrderItemVO;
import com.jf.cloud.common.order.vo.EsOrderVO;
import com.jf.cloud.common.order.vo.UserOrderStatisticVO;
import com.jf.cloud.common.product.constant.PreSaleType;
import com.jf.cloud.common.product.constant.SupplierDeliveryType;
import com.jf.cloud.common.util.Json;
import com.jf.cloud.search.constant.EsConstant;
import com.jf.cloud.search.constant.EsIndexEnum;
import com.jf.cloud.search.util.EsSearchUtil;
import ma.glasnost.orika.MapperFacade;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedMax;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.ParsedValueCount;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zz
 * @date 2020/11/16
 */
@Component
public class OrderSearchManager {

    private static final Logger log = LoggerFactory.getLogger(OrderSearchManager.class);

    /**
     * 余额支付（搜索类型）
     */
    private static final Integer SEARCH_BALANCE = 3;

    /**
     * 1、统计：消费金额 优惠总金额 消费次数
     */
    private static final Integer COUNT_TYPE_FIRST = 1;
    /**
     * 2、统计：实付金额
     */
    private static final Integer COUNT_TYPE_SECOND = 2;
    /**
     * 3、统计：最近消费时间
     */
    private static final Integer COUNT_TYPE_THIRD = 3;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private MapperFacade mapperFacade;

//    /**
//     * 通过搜索信息分页搜索es数据的信息
//     *
//     * @param orderSearchDTO 订单搜索条件
//     * @return 搜索结果
//     */
//    public List<EsOrderVO> listSearchResult(OrderSearchDTO orderSearchDTO) {
//        // 处理支付类型
//        if(Objects.nonNull(orderSearchDTO.getPayType())) {
//            addPayTypeList(orderSearchDTO);
//        }
//        // 查询订单数据
//        SearchResponse response = searchResult(orderSearchDTO);
//        // 处理响应的数据
//        List<EsOrderVO> result = getEsOrderBOList(response);
//        return result;
//    }

    private void addPayTypeList(OrderSearchDTO orderSearchDTO) {
        List<Integer> payTypeList = new ArrayList<>();
        if (PayType.isWeChatPay(orderSearchDTO.getPayType())) {
            payTypeList.addAll(Arrays.asList(PayType.weChatPayArray));
        } else if (PayType.isAliPay(orderSearchDTO.getPayType())) {
            payTypeList.addAll(Arrays.asList(PayType.aliPayArray));
        } else if (Objects.equals(PayType.BALANCE.value(), orderSearchDTO.getPayType())) {
            payTypeList.add(SEARCH_BALANCE);
        } else if (Objects.equals(PayType.SCOREPAY.value(), orderSearchDTO.getPayType())) {
            payTypeList.add(PayType.SCOREPAY.value());
        }
        orderSearchDTO.setPayTypeList(payTypeList);
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderSearchDTO 订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsOrderVO> pageSearchResult(OrderSearchDTO orderSearchDTO) {
        if (Objects.isNull(orderSearchDTO.getPageSize())) {
            orderSearchDTO.setPageSize(10);
        }
        if (Objects.isNull(orderSearchDTO.getPageNum())) {
            orderSearchDTO.setPageNum(1);
        }
        //1、查询订单数据
        SearchResponse response = searchResult(orderSearchDTO);
        //2.处理响应的数据
        return pageEsOrderBO(response, orderSearchDTO.getPageSize());
    }


    /**
     * 获取检索列表（不分页，获取全部数据）
     *
     * @param orderSearchDTO
     * @return
     */
    public List<EsOrderVO> listSearchResult(OrderSearchDTO orderSearchDTO) {
        List<SearchHits> searchHitsList = new ArrayList<>();
        int pageNum = 0;
        int pageSize = 10000;
        orderSearchDTO.setPageSize(pageSize);
        SearchResponse response;
        // es默认可获取数据最大条数10000，若命中数据超出此值，继续分页查询数据，来实现将全部数据取出
        do {
            orderSearchDTO.setPageNum(++pageNum);
            response = searchResult(orderSearchDTO);
            searchHitsList.add(response.getHits());
        } while ((long) pageNum * pageSize < response.getHits().getTotalHits().value);
        // 取出es数据
        List<EsOrderVO> orderList = new ArrayList<>(16);
        for (SearchHits searchHits : searchHitsList) {
            orderList.addAll(this.getOrderListByResponse(searchHits.getHits()));
        }
        return orderList;
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderIds
     * @return 搜索结果
     */
    public List<EsOrderVO> listByOrderIds(List<Long> orderIds) {
        return listByOrderIds(orderIds, null);
    }

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderIds
     * @return 搜索结果
     */
    public List<EsOrderVO> listByOrderIds(List<Long> orderIds, String[] fetchSource) {
        if (CollUtil.isEmpty(orderIds)) {
            return new ArrayList<>();
        }

        OrderSearchDTO orderSearchDTO = new OrderSearchDTO();
        orderSearchDTO.setOrderIds(orderIds);
        orderSearchDTO.setPageSize(orderIds.size());
        orderSearchDTO.setPageNum(1);
        if (ArrayUtil.isNotEmpty(fetchSource)) {
            orderSearchDTO.setFetchSource(fetchSource);
        }
        EsPageVO<EsOrderVO> esOrderPage = pageSearchResult(orderSearchDTO);
        return esOrderPage.getList();
    }

    private EsPageVO<EsOrderVO> pageEsOrderBO(SearchResponse response, Integer pageSize) {
        EsPageVO<EsOrderVO> pageVO = new EsPageVO<>();
        List<EsOrderVO> orderList = getOrderListByResponse(response.getHits().getHits());
        pageVO.setList(orderList);
        buildSearchPage(pageVO, response, pageSize);
        return pageVO;
    }

    /**
     * 构建分页数据
     *
     * @param esPageVO
     * @param response
     * @param pageSize
     */
    private void buildSearchPage(EsPageVO<?> esPageVO, SearchResponse response, Integer pageSize) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int) total % pageSize == 0 ?
                (int) total / pageSize : ((int) total / pageSize + 1);
        esPageVO.setPages(totalPages);
    }

    private SearchResponse searchResult(OrderSearchDTO orderSearchDTO) {
        // 封装检索条件
        SearchRequest searchRequest = buildSearchRequest(orderSearchDTO);
        SearchResponse response;
        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("搜索返回结果：" + response.toString());

        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    /**
     * 构建结果数据
     */
//    private EsPageVO<EsOrderVO> buildSearchResult(EsPageDTO pageDTO, SearchResponse response) {
//        EsPageVO<EsOrderVO> esPageVO = new EsPageVO<>();
//
//        //1、返回的所有查询到的商品
//        SearchHits hits = response.getHits();
//        List<EsOrderVO> productSearchs = getEsOrderBOList(response);
//        esPageVO.setList(productSearchs);
//
//
//        //===============分页信息====================//
//        //总记录数
//        long total = hits.getTotalHits().value;
//        esPageVO.setTotal(total);
//        // 总页码
//        int totalPages = (int) total % pageDTO.getPageSize() == 0 ?
//                (int) total / pageDTO.getPageSize() : ((int) total / pageDTO.getPageSize() + 1);
//        esPageVO.setPages(totalPages);
//        return esPageVO;
//    }
//
//    private List<EsOrderVO> getEsOrderBOList(SearchResponse response) {
//        return getOrderListByResponse(response.getHits().getHits());
//    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsOrderVO> getOrderListByResponse(SearchHit[] hits) {
        List<EsOrderVO> esOrders = new ArrayList<>();
        Integer lang = I18nMessage.getLang();
        for (SearchHit hit : hits) {
            EsOrderBO esOrderBO = Json.parseObject(hit.getSourceAsString(), EsOrderBO.class);
            EsOrderVO esOrderVO = mapperFacade.map(esOrderBO, EsOrderVO.class);
            esOrders.add(esOrderVO);
            if (CollUtil.isEmpty(esOrderBO.getOrderItems())) {
                continue;
            }
            for (EsOrderItemBO orderItem : esOrderBO.getOrderItems()) {
                if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                    orderItem.setSpuName(orderItem.getSpuNameEn());
                    orderItem.setSkuName(orderItem.getSkuNameEn());
                }
                if (StrUtil.isBlank(orderItem.getSpuName())) {
                    orderItem.setSpuName(orderItem.getSpuNameZh());
                }
                if (StrUtil.isBlank(orderItem.getSkuName())) {
                    orderItem.setSkuName(orderItem.getSkuNameZh());
                }
            }
            esOrderVO.setOrderItems(mapperFacade.mapAsList(esOrderBO.getOrderItems(), EsOrderItemVO.class));
        }
        return esOrders;
    }


    /**
     * 准备检索请求
     *
     * @param param 搜索参数
     * @return
     */
    private SearchRequest buildSearchRequest(OrderSearchDTO param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        }

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索
        keywordSearch(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        if (Objects.nonNull(param.getPageNum()) && Objects.nonNull(param.getPageSize())) {
            //分页
            searchSourceBuilder.from((param.getPageNum() - 1) * param.getPageSize());
            searchSourceBuilder.size(param.getPageSize());
        }

        log.info("构建的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.ORDER.value()}, searchSourceBuilder);
    }


    /**
     * 关键字搜索
     */
    private void keywordSearch(OrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {

        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可


        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();

        // 店铺名称
        if (StrUtil.isNotBlank(param.getShopName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("shopName", param.getShopName()));
        }

        // 店铺区县地址
        if (StrUtil.isNotBlank(param.getShopAddr())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("shopAddr", param.getShopAddr()));
        }

        // 商品名称
        if (StrUtil.isNotBlank(param.getSpuName())) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            boolQuery.should(QueryBuilders.matchQuery("orderItems.spuNameZh", param.getSpuName()));
            boolQuery.should(QueryBuilders.matchQuery("orderItems.spuNameEn", param.getSpuName()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("orderItems", boolQuery, ScoreMode.None);
            boolQueryBuilder.must(nestedQueryBuilder);
        }

        //收货人姓名
        if (StrUtil.isNotBlank(param.getConsignee())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("consignee", param.getConsignee()));
        }

        //收货人手机号
        if (StrUtil.isNotBlank(param.getMobile())) {
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery()
                    .should(QueryBuilders.matchPhraseQuery("mobile", param.getMobile()));
            boolQueryBuilder.must(shouldQuery);
//            keywordShouldQuery.should(QueryBuilders.matchQuery("mobile", param.getMobile()));
        }

        // 商品编码
        if (StrUtil.isNotBlank(param.getSpuCodes())) {
            keywordShouldQuery.should(QueryBuilders.termQuery("spuCodes", param.getSpuCodes()));
        }

        // sku编码
        if (StrUtil.isNotBlank(param.getPartyCodes())) {
            BoolQueryBuilder partyCodesShouldQuery = QueryBuilders.boolQuery()
                    .should(QueryBuilders.matchPhraseQuery("partyCodes", param.getPartyCodes()));
            boolQueryBuilder.must(partyCodesShouldQuery);
//            keywordShouldQuery.should(QueryBuilders.matchQuery("partyCodes", param.getPartyCodes()));
        }

        //自提点名称
        if (StrUtil.isNotBlank(param.getStationName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("stationName", param.getStationName()));
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     *
     * @param param            查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(OrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {
        if (Objects.nonNull(param.getIsComm())) {
            param.setStatus(OrderStatus.SUCCESS.value());
        }
        if (Objects.nonNull(param.getSupplierDeliveryType())) {
            if (Objects.equals(param.getSupplierDeliveryType(), SupplierDeliveryType.SHOP_DELIVERY.value())) {
                boolQueryBuilder.mustNot(QueryBuilders.termQuery("supplierDeliveryType", SupplierDeliveryType.SUPPLIER_DELIVERY.value()));
            } else {
                boolQueryBuilder.filter(QueryBuilders.termQuery("supplierDeliveryType", param.getSupplierDeliveryType()));
            }
        }
        // 店铺id
        if (Objects.nonNull(param.getShopId()) && !Objects.equals(param.getShopId(), Constant.PLATFORM_SHOP_ID)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId()));
        }
        // 店铺id集合
        if (CollUtil.isNotEmpty(param.getShopIds())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("shopId", param.getShopIds()));
        }
        // 供应商id
        if (Objects.nonNull(param.getSupplierId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("waitPurchase", 1));
        } else if (Objects.equals(param.getWaitPurchase(), 1)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("waitPurchase", param.getWaitPurchase()));
        }

        // 供应商id集合
        if (CollUtil.isNotEmpty(param.getSupplierIds())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("supplierId", param.getSupplierIds()));
        }

        if (Objects.equals(param.getWaitPurchase(), 0)) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("waitPurchase", 1));
        }
        // 用户id
        if (Objects.nonNull(param.getUserId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("userId", param.getUserId()));
        }
        //平台端会员详情不展示未付款的订单
        if (Objects.equals(param.getShopId(), Constant.PLATFORM_SHOP_ID) && Objects.nonNull(param.getUserId())) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("status", OrderStatus.UNPAY.value()));
        }
        // 订单id
        if (param.getOrderId() != null) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("orderId", param.getOrderId().toString()));
        }
        // 订单地区id
        if (Objects.nonNull(param.getAreaCode())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("shopAddrId", param.getAreaCode()));
        }

        /**
         * 订单id列表
         */
        if (CollectionUtil.isNotEmpty(param.getOrderIds())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("orderId", param.getOrderIds()));
        }

        /**
         * 订单项id列表
         */
        if (CollectionUtil.isNotEmpty(param.getOrderItemIds())) {
//            boolQueryBuilder.must(QueryBuilders.termsQuery("orderItems.orderItemId",param.getOrderItemIds()));
            boolQueryBuilder.filter(EsSearchUtil.nestedQuery("orderItems", "orderItems.orderItemId", param.getOrderItemIds()));
        }

        /**
         * 判断是否是虚拟订单或自提订单
         */
        if (param.getStationId() != null || param.getOrderMold() != null) {
            if (param.getStationId() == null) {
                // 虚拟订单
                boolQueryBuilder.filter(QueryBuilders.termQuery("orderMold", param.getOrderMold()));
            } else if (param.getOrderMold() == null) {
                // 自提订单以及部分核销过的虚拟订单
                BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
                BoolQueryBuilder stationQuery = QueryBuilders.boolQuery();
                stationQuery.must(QueryBuilders.termsQuery("stationId", param.getStationId().toString()));
                BoolQueryBuilder codeShouldQuery = QueryBuilders.boolQuery();
                codeShouldQuery.must(QueryBuilders.matchQuery("orderVirtualInfoList.stationId", param.getStationId()));
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("orderVirtualInfoList", codeShouldQuery, ScoreMode.None);
                shouldQuery.should(nestedQueryBuilder);
                shouldQuery.should(stationQuery);
                boolQueryBuilder.must(shouldQuery);
                //boolQueryBuilder.filter(QueryBuilders.termsQuery("stationId", param.getStationId().toString()));
            } else {
                // 虚拟订单加自提订单
                BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
                // or条件用should查询，必须配合must使用
                BoolQueryBuilder virtualQuery = QueryBuilders.boolQuery();
                BoolQueryBuilder rangeQuery = QueryBuilders.boolQuery();
                rangeQuery.should(QueryBuilders.rangeQuery("writeOffEnd").gt(new Date()));
                BoolQueryBuilder existsQuery = QueryBuilders.boolQuery();
                rangeQuery.should(existsQuery.mustNot(QueryBuilders.existsQuery("writeOffEnd")));
                virtualQuery.must(rangeQuery);
                virtualQuery.filter(QueryBuilders.termQuery("orderMold", param.getOrderMold()));
                virtualQuery.must(QueryBuilders.termsQuery("status", Arrays.asList(
                        OrderStatus.CONSIGNMENT.value(),
                        OrderStatus.SUCCESS.value())
                ));
                virtualQuery.mustNot(QueryBuilders.termQuery("writeOffNum", 0));
                virtualQuery.must(QueryBuilders.termQuery("writeOffStatus", 0));

                BoolQueryBuilder stationQuery = QueryBuilders.boolQuery();
                stationQuery.filter(QueryBuilders.termsQuery("stationId", param.getStationId().toString()));
                stationQuery.must(QueryBuilders.termQuery("status", OrderStatus.PAYED.value()));
                shouldQuery.should(virtualQuery);
                shouldQuery.should(stationQuery);
                boolQueryBuilder.must(shouldQuery);
            }
        }

        /**
         * 自提提货码
         */
        if (StrUtil.isNotBlank(param.getStationCode())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("stationCode", param.getStationCode()));
        }
        /**
         * 核销码
         */
        if (StrUtil.isNotBlank(param.getWriteOffCode())) {
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();

            // or条件用should查询，必须配合must使用
            BoolQueryBuilder idShouldQuery = QueryBuilders.boolQuery();
            idShouldQuery.must(QueryBuilders.termQuery("orderId", param.getWriteOffCode()));
            BoolQueryBuilder codeShouldQuery = QueryBuilders.boolQuery();
            codeShouldQuery.must(QueryBuilders.matchQuery("orderVirtualInfoList.writeOffCode", param.getWriteOffCode()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("orderVirtualInfoList", codeShouldQuery, ScoreMode.None);
            shouldQuery.should(idShouldQuery);
            shouldQuery.should(nestedQueryBuilder);
            BoolQueryBuilder rangBuilder = QueryBuilders.boolQuery();
            rangBuilder.should(QueryBuilders.rangeQuery("writeOffEnd").gt(new Date()));
            BoolQueryBuilder existsBuilder = QueryBuilders.boolQuery();
            rangBuilder.should(existsBuilder.mustNot(QueryBuilders.existsQuery("writeOffEnd")));
            boolQueryBuilder.must(shouldQuery);
            boolQueryBuilder.must(rangBuilder);
        }
        /**
         * 核销状态
         */
        if (param.getWriteOffStatus() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("writeOffStatus", param.getWriteOffStatus()));
        }
        // 删除类型
        if (Objects.nonNull(param.getDeleteStatus())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("deleteStatus", param.getDeleteStatus()));
        }
        if (Objects.equals(param.getStatus(), OrderStatus.UNPAY.value())) {
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
            // or条件用should查询，必须配合must使用
            shouldQuery.should(QueryBuilders.matchQuery("status", OrderStatus.UNPAY.value()));
            shouldQuery.should(QueryBuilders.matchQuery("status", OrderStatus.WAIT_BALANCE.value()));
            boolQueryBuilder.must(shouldQuery);
        }
        // 订单状态 参考OrderStatus
        if (Objects.nonNull(param.getStatus()) && !Objects.equals(param.getStatus(), 0) && !Objects.equals(param.getStatus(), OrderStatus.UNPAY.value())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("status", param.getStatus()));
        }
        // 订单状态 参考OrderStatus
        if (CollUtil.isNotEmpty(param.getStatusList())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("status", param.getStatusList()));
        }
        //平台端会员详情不展示未付款的订单
        if (CollUtil.isNotEmpty(param.getMustNotStatusList())) {
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery("status", param.getMustNotStatusList()));
        }
        //订单类型参考orderType ,1团购订单 2秒杀订单,3积分订单
        if (Objects.nonNull(param.getOrderType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("orderType", param.getOrderType()));
        }
        // 商品类型为空，且用户id为空(orderIds有值时，是查询指定的订单，不需要订单类型的筛选) -> 商家、平台端获取的非积分订单列表
        else if (Objects.isNull(param.getUserId()) && CollUtil.isEmpty(param.getOrderIds()) && CollUtil.isEmpty(param.getOrderItemIds())) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("orderType", OrderType.SCORE.value()));
        }

        // 是否已经支付，1：已经支付过，0：，没有支付过
        if (Objects.nonNull(param.getIsPayed())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("isPayed", param.getIsPayed()));
        }


        // 是否已经进行结算
        if (Objects.nonNull(param.getIsSettled())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("isSettled", param.getIsSettled()));
        }

        // 支付时间
        if (Objects.nonNull(param.getPayTime())) {
            RangeQueryBuilder rangeQueryBuilder;
            if (BooleanUtil.isTrue(param.getGtePayTime().booleanValue())) {
                rangeQueryBuilder = QueryBuilders.rangeQuery("payTime").gte(param.getPayTime());
            } else {
                rangeQueryBuilder = QueryBuilders.rangeQuery("payTime").lte(param.getPayTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 发货时间
        if (Objects.nonNull(param.getDeliveryTime())) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("deliveryTime").lte(param.getDeliveryTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 确认收货时间
        if (Objects.nonNull(param.getFinallyTime())) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("finallyTime").lte(param.getFinallyTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 预售开始时间
        if (Objects.nonNull(param.getBalanceStartTime())) {
            RangeQueryBuilder rangeStartQueryBuilder = QueryBuilders.rangeQuery("balanceStartTime").lte(param.getBalanceStartTime());
            RangeQueryBuilder rangeEndQueryBuilder = QueryBuilders.rangeQuery("balanceEndTime").gte(param.getBalanceStartTime());
            boolQueryBuilder.filter(rangeStartQueryBuilder);
            boolQueryBuilder.filter(rangeEndQueryBuilder);
        }

        // 预售结束时间
        if (Objects.nonNull(param.getBalanceEndTime())) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("balanceEndTime").lte(param.getBalanceEndTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
        List<Integer> preSaleTypeList = new ArrayList<>();


        // 预售类型 0.全款预售类型 1.定金预售
        if (Objects.nonNull(param.getPreSaleType())) {
            preSaleTypeList.add(param.getPreSaleType());
        }

        // 是否预售订单 1.是 0.否
        if (Objects.nonNull(param.getIsPreSale()) && Objects.equals(param.getIsPreSale(), 1)) {
            // 预售类型 -1.未开启预售 0.全款预售类型 1.定金预售
            preSaleTypeList.add(0);
            preSaleTypeList.add(1);
        } else if (Objects.nonNull(param.getIsPreSale()) && Objects.equals(param.getIsPreSale(), 0)) {
            preSaleTypeList.add(-1);
        }
        if (CollUtil.isNotEmpty(preSaleTypeList)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("preSaleType", preSaleTypeList));
        }

        if (Objects.nonNull(param.getMaxBalanceAmount())) {
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("balanceAmount").lte(param.getMaxBalanceAmount());
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 开始时间 - 结束时间
        if (Objects.nonNull(param.getStartTime()) || Objects.nonNull(param.getEndTime())) {
            String createTime = "createTime";
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime);
            if (Objects.nonNull(param.getStartTime())) {
                rangeQueryBuilder.gte(param.getStartTime());
            }
            if (Objects.nonNull(param.getEndTime())) {
                rangeQueryBuilder.lte(param.getEndTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }


        // 物流类型  1:快递 2:自提 3：无需快递 4：同城快递
        if (Objects.nonNull(param.getDeliveryType())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("deliveryType", param.getDeliveryType()));
        }

        // 是否预售订单 1.是 0.否
        if (Objects.equals(param.getIsPreSale(), 1)) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("preSaleType", PreSaleType.DISABLE.value()));
        }
        if (Objects.equals(param.getIsPreSale(), 0)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("preSaleType", PreSaleType.DISABLE.value()));
        }
        // 订单退款状态（0:暂无物流 1:申请退款 2:退款成功 3:部分退款成功 4:退款失败）
        if (Objects.equals(param.getRefundStatus(), 0)) {
            boolQueryBuilder.mustNot(QueryBuilders.existsQuery("orderItems.refundStatus"));
        } else if (Objects.nonNull(param.getRefundStatus())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("refundStatus", param.getRefundStatus()));
        }


        if (Objects.nonNull(param.getNotRefundStatus())) {
            boolQueryBuilder.mustNot(QueryBuilders.termQuery("refundStatus", param.getNotRefundStatus()));
        }

//        // 支付类型  1:积分支付 2:微信支付 3：支付宝支付
//        if (Objects.nonNull(param.getPayType())) {
//            boolQueryBuilder.filter(QueryBuilders.termQuery("payType", param.getPayType()));
//        }

        // 支付类型  1:积分支付 2:微信支付 3：支付宝支付
        if (CollUtil.isNotEmpty(param.getPayTypeList())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("payType", param.getPayTypeList()));
        }

        // 评论
        if (Objects.nonNull(param.getIsComm())) {
            if (Objects.equals(param.getIsComm(), 0)) {
                boolQueryBuilder.mustNot(QueryBuilders.termQuery("refundStatus", 1));
                boolQueryBuilder.mustNot(QueryBuilders.termQuery("orderType", OrderType.SCORE.value()));
            }
            // 退款状态为退款失败或者为空的
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
            boolQuery.must(QueryBuilders.matchQuery("orderItems.isComm", param.getIsComm()));
            // or条件用should查询，必须配合must使用
            shouldQuery.should(QueryBuilders.matchQuery("orderItems.refundStatus", 4));
            shouldQuery.should(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery("orderItems.refundStatus")));
            boolQuery.must(shouldQuery);
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("orderItems", boolQuery, ScoreMode.None);
            boolQueryBuilder.filter(nestedQueryBuilder);
        }
    }

    /**
     * 统计用户的订单相关信息
     *
     * @param userIds 用户id集合
     * @return 用户的订单相关统计数据
     */
    public List<UserOrderStatisticVO> countOrderByUserIds(List<Long> userIds) {
        if (CollUtil.isEmpty(userIds)) {
            return null;
        }
        List<UserOrderStatisticVO> userOrderStatistics = new ArrayList<>();
        //1、准备检索请求

        // 1统计：消费金额 优惠总金额 消费次数
        SearchRequest searchRequest1 = buildCountOrderSearchRequest(userIds, COUNT_TYPE_FIRST);
        // 2统计：实付金额
        SearchRequest searchRequest2 = buildCountOrderSearchRequest(userIds, COUNT_TYPE_SECOND);
        // 3统计：最近消费时间
        SearchRequest searchRequest3 = buildCountOrderSearchRequest(userIds, COUNT_TYPE_THIRD);
        try {
            //2、执行检索请求
            SearchResponse response1 = restHighLevelClient.search(searchRequest1, RequestOptions.DEFAULT);
            SearchResponse response2 = restHighLevelClient.search(searchRequest2, RequestOptions.DEFAULT);
            SearchResponse response3 = restHighLevelClient.search(searchRequest3, RequestOptions.DEFAULT);

            log.debug("消费金额 优惠总金额 消费次数,搜索返回结果：" + response1.toString());
            log.debug("实付金额,搜索返回结果：" + response2.toString());
            log.debug("最近消费时间,搜索返回结果：" + response3.toString());

            //3、分析响应数据，封装成我们需要的格式
            userOrderStatistics = combinedEsOrderAggData(userOrderStatistics, userIds, response1, response2, response3);
        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return userOrderStatistics;
    }


    private List<UserOrderStatisticVO> combinedEsOrderAggData(List<UserOrderStatisticVO> userOrderStatistics, List<Long> userIds, SearchResponse response1, SearchResponse response2, SearchResponse response3) {

        ParsedLongTerms userIdTerms1 = response1.getAggregations().get("userIdTerms");
        ParsedLongTerms userIdTerms2 = response2.getAggregations().get("userIdTerms");
        ParsedLongTerms userIdTerms3 = response3.getAggregations().get("userIdTerms");

        Map<Number, Terms.Bucket> buckets1Map = userIdTerms1.getBuckets().stream().collect(Collectors.toMap(Terms.Bucket::getKeyAsNumber, (k) -> k));
        Map<Number, Terms.Bucket> buckets2Map = userIdTerms2.getBuckets().stream().collect(Collectors.toMap(Terms.Bucket::getKeyAsNumber, (k) -> k));
        Map<Number, Terms.Bucket> buckets3Map = userIdTerms3.getBuckets().stream().collect(Collectors.toMap(Terms.Bucket::getKeyAsNumber, (k) -> k));
        if (buckets1Map.size() == 0 && buckets2Map.size() == 0 && buckets3Map.size() == 0) {
            return userOrderStatistics;
        }
        for (Long userId : userIds) {
            UserOrderStatisticVO userOrderStatisticVO = new UserOrderStatisticVO();
            userOrderStatisticVO.setUserId(userId);
            Terms.Bucket bucket1 = buckets1Map.get(userId);
            if (Objects.nonNull(bucket1)) {

                // 消费金额
                Aggregations aggregations = bucket1.getAggregations();
                ParsedSum actualTotalAgg = aggregations.get("actualTotalAgg");
                double consAmount = actualTotalAgg.getValue();
                userOrderStatisticVO.setConsAmount(new BigDecimal(Double.valueOf(consAmount).toString()));

                // 优惠总金额
                ParsedSum reduceAmountAgg = aggregations.get("reduceAmountAgg");
                double reduceAmount = reduceAmountAgg.getValue();
                userOrderStatisticVO.setReduceAmount(new BigDecimal(Double.valueOf(reduceAmount).toString()));

                // 消费次数
                ParsedValueCount consTimesAgg = aggregations.get("consTimesAgg");
                long consTimes = consTimesAgg.getValue();
                userOrderStatisticVO.setConsTimes(Long.valueOf(consTimes).intValue());
            } else {
                userOrderStatisticVO.setConsAmount(new BigDecimal("0.0"));
                userOrderStatisticVO.setReduceAmount(new BigDecimal("0.0"));
                userOrderStatisticVO.setConsTimes(0);
            }
            // 实付金额
            Terms.Bucket bucket2 = buckets2Map.get(userId);
            if (Objects.nonNull(bucket2)) {
                ParsedSum actualTotalAgg = bucket2.getAggregations().get("actualTotalAgg");
                double actualTotal = actualTotalAgg.getValue();
                userOrderStatisticVO.setActualAmount(new BigDecimal(Double.valueOf(actualTotal).toString()));
            } else {
                userOrderStatisticVO.setActualAmount(new BigDecimal("0.0"));
            }
            // 最新消费时间
            Terms.Bucket bucket3 = buckets3Map.get(userId);
            if (Objects.nonNull(bucket3)) {
                ParsedMax payTime = bucket3.getAggregations().get("maxPayTime");
                String payTimeStr = payTime.getValueAsString();
                userOrderStatisticVO.setReConsTime(DateUtil.parse(payTimeStr));
            }
            userOrderStatistics.add(userOrderStatisticVO);
        }

        return userOrderStatistics;
    }


    /**
     * @param countType 1、统计：消费金额 优惠总金额 消费次数
     *                  2、统计：实付金额
     *                  3、统计：最近消费时间
     * @return
     */
    private SearchRequest buildCountOrderSearchRequest(List<Long> userIds, int countType) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchQuery("isPayed", 1));
        boolQueryBuilder.must(QueryBuilders.matchQuery("deleteStatus", 0));
        boolQueryBuilder.must(QueryBuilders.termsQuery("userId", userIds));
        if (countType == COUNT_TYPE_FIRST) {
            boolQueryBuilder.mustNot(QueryBuilders.matchQuery("payType", PayType.SCOREPAY.value()));
        } else if (countType == COUNT_TYPE_SECOND) {
            List<Integer> payTypes = new ArrayList<>();
            payTypes.add(PayType.SCOREPAY.value());
            payTypes.add(PayType.BALANCE.value());
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery("payType", payTypes));
        }

        // 聚合条件
        // 1. 按照userId进行聚合（分组）
        TermsAggregationBuilder userIdAgg = AggregationBuilders.terms("userIdTerms");
        userIdAgg.field("userId").size(userIds.size());
        if (countType == COUNT_TYPE_FIRST) {
            // 消费金额
            userIdAgg.subAggregation(AggregationBuilders.sum("actualTotalAgg").field("actualTotal"));
            // 优惠总金额
            userIdAgg.subAggregation(AggregationBuilders.sum("reduceAmountAgg").field("reduceAmount"));
            // 消费次数
            userIdAgg.subAggregation(AggregationBuilders.count("consTimesAgg").field("orderId"));
        } else if (countType == COUNT_TYPE_SECOND) {
            // 实付金额
            userIdAgg.subAggregation(AggregationBuilders.sum("actualTotalAgg").field("actualTotal"));
        } else if (countType == COUNT_TYPE_THIRD) {
            // 最近消费时间
            userIdAgg.subAggregation(AggregationBuilders.max("maxPayTime").field("payTime"));
        }

        searchSourceBuilder.aggregation(userIdAgg);
        // 聚合数据，不需要获取命中的结果，只需要聚合的结果
        searchSourceBuilder.size(0);
        searchSourceBuilder.query(boolQueryBuilder);

        log.info("构建的DSL语句 {}", searchSourceBuilder.toString());
        return new SearchRequest(new String[]{EsIndexEnum.ORDER.value()}, searchSourceBuilder);
    }

    public List<Map<String, Object>> sumOrderGroupArea(OrderSearchDTO orderSearchDTO) {
        List<Map<String, Object>> areaMapList = new ArrayList<>();
        // 设置查询超时时间
        SearchRequest searchRequest = buildSumOrderSearchRequest(orderSearchDTO);
        try {
            SearchResponse response1 = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            areaMapList = combinedEsOrderAreaAggData(areaMapList, response1);
        } catch (Exception e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return areaMapList;
    }

    private List<Map<String, Object>> combinedEsOrderAreaAggData(List<Map<String, Object>> esOrderVOList, SearchResponse response1) {

        ParsedLongTerms areaIdTerms = response1.getAggregations().get("areaIdTerms");

        Map<Number, Terms.Bucket> buckets1Map = areaIdTerms.getBuckets().stream().collect(Collectors.toMap(Terms.Bucket::getKeyAsNumber, (k) -> k));
        if (buckets1Map.size() == 0) {
            return esOrderVOList;
        }
        for (int i = 0; i < areaIdTerms.getBuckets().size(); i++) {
            Terms.Bucket bucket1 = areaIdTerms.getBuckets().get(i);
            Map<String, Object> esOrderVO = new HashMap<>();
            if (Objects.nonNull(bucket1)) {
                // 消费金额
                Aggregations aggregations = bucket1.getAggregations();
                ParsedSum actualTotalAgg = aggregations.get("actualTotalAgg");
                double consAmount = actualTotalAgg.getValue();
                esOrderVO.put("actualTotal", consAmount);
                esOrderVO.put("areaId", bucket1.getKey());

            }
            esOrderVOList.add(esOrderVO);
        }
        return esOrderVOList;
    }

    private SearchRequest buildSumOrderSearchRequest(OrderSearchDTO orderSearchDTO) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.must(QueryBuilders.matchQuery("isPayed", 1));
        boolQueryBuilder.must(QueryBuilders.matchQuery("deleteStatus", 0));
        // 支付时间
        if (Objects.nonNull(orderSearchDTO.getStartTime()) && Objects.nonNull(orderSearchDTO.getEndTime())) {
            RangeQueryBuilder rangeQueryBuilder;
            rangeQueryBuilder = QueryBuilders.rangeQuery("payTime").gte(orderSearchDTO.getStartTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
            rangeQueryBuilder = QueryBuilders.rangeQuery("payTime").lte(orderSearchDTO.getEndTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
        boolQueryBuilder.mustNot(QueryBuilders.matchQuery("payType", PayType.SCOREPAY.value()));
        // 聚合条件
        // 1. 按照areaId进行聚合（分组）
        TermsAggregationBuilder areaIdAgg = AggregationBuilders.terms("areaIdTerms");
        areaIdAgg.field("shopAddrId");
        // 消费金额
        areaIdAgg.subAggregation(AggregationBuilders.sum("actualTotalAgg").field("actualTotal"));
        searchSourceBuilder.aggregation(areaIdAgg);
        // 聚合数据，不需要获取命中的结果，只需要聚合的结果
        searchSourceBuilder.size(0);
        searchSourceBuilder.query(boolQueryBuilder);
        log.info("buildSumOrderSearchRequest构建的DSL语句 {}", searchSourceBuilder.toString());
        return new SearchRequest(new String[]{EsIndexEnum.ORDER.value()}, searchSourceBuilder);
    }

    public SearchRequest buildScrollSearchRequest(OrderSearchDTO param, Scroll scroll) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 指定返回数组中的字段
        if (ArrayUtil.isNotEmpty(param.getFetchSource())) {
            searchSourceBuilder.fetchSource(param.getFetchSource(), null);
        }
        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        //设置最多一次能够取出100笔数据，从第101笔数据开始，将开启滚动查询
        //PS:滚动查询也属于这一次查询，只不过因为一次查不完，分多次查
        if (Objects.nonNull(param.getPageSize()) && param.getPageSize() > 0) {
            searchSourceBuilder.size(param.getPageSize());
        } else {
            searchSourceBuilder.size(EsConstant.MAX_PAGE_SIZE);
        }
        log.info("构建滚动查询的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.ORDER.value()}, searchSourceBuilder).scroll(scroll);
    }
}
